package org.hikari.qqbot.xiuxian.command;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.hikari.qqbot.client.IMessageClient;
import org.hikari.qqbot.client.LLR;
import org.hikari.qqbot.client.dto.GroupMsgRequest;
import org.hikari.qqbot.client.dto.GroupMsgResponse;
import org.hikari.qqbot.client.dto.PrivateMsgRequest;
import org.hikari.qqbot.client.dto.PrivateMsgResponse;
import org.hikari.qqbot.client.message.MessageSegment;
import org.hikari.qqbot.client.message.segment.AtMessageSegment;
import org.hikari.qqbot.client.message.segment.TextMessageSegment;
import org.hikari.qqbot.xiuxian.util.MessageUtil;

import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.DelayQueue;
import java.util.function.Predicate;
import java.util.stream.Stream;

/**
 * created at 2024-12-18 03:55:46
 *
 * @author yinchao
 * @version 1.0
 **/
@Slf4j
@RequiredArgsConstructor
public class CommandRunner {

    private final String name;
    private final IMessageClient messageClient;
    private final DelayQueue<ExecutableCommand> queue = new DelayQueue<>();

    private volatile boolean running = false;

    public void sendCommand(ExecutableCommand command) {
        if (command.getSendTime() == null) {
            doCommandSend(command);
        } else {
            addCommand(command);
        }
    }

    private void doCommandSend(ExecutableCommand command) {
        long begin = System.currentTimeMillis();
        try {
            // 私聊消息
            if (command.getGroupId() == null) {
                // 直接向修仙之路机器人发送指令
                List<MessageSegment> segments = new ArrayList<>(1);
                segments.add(new TextMessageSegment(command.getCommand()).toSegment());
                PrivateMsgRequest request = new PrivateMsgRequest();
                request.setUser_id(command.getUserId());
                request.setMessage(segments);
                LLR<PrivateMsgResponse> r = messageClient.sendPrivateMsg(request);
                log.info("用户[{}] 命令[{}] 发送到 修仙之路[{}]{} 耗时: {}ms.", command.getSelfId(), command.getCommand(), request.getUser_id(),
                        r.isOk() ? "成功" : "失败, 错误: " + r.getStatus() + " - " + r.getMessage(),
                        System.currentTimeMillis() - begin);
            }
            // 群聊消息
            else {
                // 在群里发送[@修仙之路 指令]
                List<MessageSegment> segments = new ArrayList<>(2);
                segments.add(new AtMessageSegment(command.getUserId()).toSegment());
                segments.add(new TextMessageSegment(" " + command.getCommand()).toSegment());
                GroupMsgRequest request = new GroupMsgRequest();
                request.setGroup_id(command.getGroupId());
                request.setMessage(segments);
                LLR<GroupMsgResponse> r = messageClient.sendGroupMsg(request);
                log.info("用户[{}] 命令[@{} {}] 发送到 QQ群[{}]{} 耗时: {}ms. ", command.getSelfId(), command.getUserId(), command.getCommand(), request.getGroup_id(),
                        r.isOk() ? "成功" : "失败, 错误: " + r.getStatus() + " - " + r.getMessage(),
                        System.currentTimeMillis() - begin);
            }
            executeCallbackSuccess(command, begin);
        } catch (Throwable e) {
            log.error("命令: {} 发送失败! 耗时: {}ms. ", command, System.currentTimeMillis() - begin, e);
            executeCallbackError(command, begin, e);
        }
    }

    private void executeCallbackSuccess(ExecutableCommand command, long begin) {
        ICommandSendCallback callback = command.getCallback();
        if (callback != null) {
            try {
                CommandExecutionInfo info = CommandExecutionInfo.from(command, new Date(begin));
                callback.onSuccess(info);
            } catch (Throwable e) {
                log.error("执行回调类[{}] onSuccess方法异常! 命令: {}", callback.getClass().getName(), command, e);
            }
        }
    }

    private void executeCallbackError(ExecutableCommand command, long begin, Throwable ex) {
        ICommandSendCallback callback = command.getCallback();
        if (callback != null) {
            try {
                CommandExecutionInfo info = CommandExecutionInfo.from(command, new Date(begin));
                callback.onError(info, ex);
            } catch (Throwable e) {
                log.error("执行回调类[{}] onError方法异常! 命令: {} 回调异常信息: {}", callback.getClass().getName(), command, ex.getMessage(), e);
            }
        }
    }

    private void addCommand(ExecutableCommand command) {
        // 更新已经存在的命令
        ExecutableCommand deleted = deleteExistsCommand(command.getCommand());
        if (deleted != null) {
            log.info("[{} - {}] 发送队列已存在相同命令 [{}] 发送时间更新 {} => {}", command.getSelfId(), command.getTaskId(), command.getCommand(), MessageUtil.getDisplaySendTime(deleted.getSendTime()), MessageUtil.getDisplaySendTime(command.getSendTime()));
        }
        queue.add(command);
        log.info("命令添加到发送队列: {}", command);
    }

    private ExecutableCommand deleteExistsCommand(String command) {
        Iterator<ExecutableCommand> iterator = queue.iterator();
        while (iterator.hasNext()) {
            ExecutableCommand data = iterator.next();
            // 如果队列中存在相同命令
            if (data.getCommand().equals(command)) {
                // 老的命令删掉
                iterator.remove();
                return data;
            }
        }
        return null;
    }

    public int deleteCommandIf(Predicate<ExecutableCommand> predicate) {
        int count = 0;
        Iterator<ExecutableCommand> iterator = queue.iterator();
        while (iterator.hasNext()) {
            ExecutableCommand data = iterator.next();
            if (predicate.test(data)) {
                iterator.remove();
                count++;
            }
        }
        return count;
    }

    public Stream<ExecutableCommand> searchCommand(Predicate<ExecutableCommand> predicate) {
        return queue.stream()
                .filter(predicate);
    }

    public void start() {
        if (running) {
            log.info("[CommandRunner - {}] ignore 'start' operate. current runner already started", name);
            return;
        }
        synchronized (this) {
            if (running) {
                log.info("[CommandRunner - {}] ignore 'start' operate. current runner already started", name);
                return;
            }
            this.running = true;
            new Thread(this::consumeQueue, "CommandRunner - " + name).start();
            log.info("[CommandRunner - {}] started", name);
        }
    }

    public void stop() {
        if (!running) {
            log.info("[CommandRunner - {}] ignore 'stop' operate. current runner already stopped", name);
            return;
        }
        synchronized (this) {
            if (!running) {
                log.info("[CommandRunner - {}] ignore 'stop' operate. current runner already stopped", name);
                return;
            }
            queue.clear();
            this.running = false;
            log.info("[CommandRunner - {}] stopping", name);
        }
    }

    private void consumeQueue() {
        while (running) {
            try {
                ExecutableCommand command = queue.take();
                this.doCommandSend(command);
            } catch (Exception e) {
                log.error("[consumeQueue - {} error! {}", name, e.getMessage(), e);
            }
        }
        log.info("[CommandRunner - {}] stopped", name);
    }

}
